【改】AWS Lambda で MeCab を動かす
木戸です。
前回ご紹介した「AWS Lambda で MeCab を動かす」のエントリーですが、通りすがりの shogo82148 さんに素敵なプルリクエストをいただきましたので、今回はそのご紹介です。
改善点
改善点は以下の2点です。
- 「外部プロセス起動をやめて高速化」
- MeCabは非常に高速なのですが、外部プロセス起動をやめることでさらに高速化しています。
- 「OSコマンドインジェクションの危険性の改善」
- Amazon API Gateway などで外部に公開した場合のOSコマンドインジェクションの危険性を改善しています。
ソースの解説
前回ご紹介したサンプルコードでは、LD_LIBRARY_PATH
を使って、import MeCab
時に libmecab.so
ライブラリを見つけられるようにするため、外部プロセスとして起動していました。(そもそも、Lambda handler 起動する前に LD_LIBRATY_PATH
設定できればこんなことしなくても良いのですが。。)
改善後は、import MeCab
する前に、ctypes.cdll.LoadLibrary
を使って libmecab.so
ライブラリを直接読み込むことで、LD_LIBRARY_PATH
が設定されていなくても MeCab のモジュールとそのライブラリがリンクできるようになっています。そのため上記の改善と、MeCab を実行するモジュールを外部に切り出す必要も無くなり、lambda_function.py
と tokenize.py
モジュールが統合され、コード的にもかなりシンプルになっています!
lambda_function.py(tokenize.py 統合)
# coding=utf-8 import os import settings import logging logger = logging.getLogger(__name__) logger.setLevel(settings.LOG_LEVEL) # preload libmecab import ctypes libdir = os.path.join(os.getcwd(), 'local', 'lib') libmecab = ctypes.cdll.LoadLibrary(os.path.join(libdir, 'libmecab.so')) import MeCab # prepare Tagger dicdir = os.path.join(os.getcwd(), 'local', 'lib', 'mecab', 'dic', 'ipadic') rcfile = os.path.join(os.getcwd(), 'local', 'etc', 'mecabrc') default_tagger = MeCab.Tagger("-d{} -r{}".format(dicdir, rcfile)) unk_tagger = MeCab.Tagger("-d{} -r{} --unk-feature 未知語,*,*,*,*,*,*,*,*".format(dicdir, rcfile)) DEFAULT_STOPTAGS = ['BOS/EOS'] def lambda_handler(event, context): sentence = event.get('sentence', '').encode('utf-8') stoptags = event.get('stoptags', '').encode('utf-8').split(',') + DEFAULT_STOPTAGS unk_feature = event.get('unk_feature', False) tokens = [] tagger = unk_tagger if unk_feature else default_tagger node = tagger.parseToNode(sentence) while node: feature = node.feature + ',*,*' part_of_speech = get_part_of_speech(feature) reading = get_reading(feature) base_form = get_base_form(feature) token = { "surface": node.surface.decode('utf-8'), "feature": node.feature.decode('utf-8'), "pos": part_of_speech.decode('utf-8'), "reading": reading.decode('utf-8'), "baseform": base_form.decode('utf-8'), "stat": node.stat, } if part_of_speech not in stoptags: tokens.append(token) node = node.next return {"tokens": tokens} def get_part_of_speech(feature): return '-'.join([v for v in feature.split(',')[:4] if v != '*']) def get_reading(feature): return feature.split(',')[7] def get_base_form(feature): return feature.split(',')[6]
まとめ
AWS Lambda Python で LD_LIBRARY_PATH
の設定が必要な時は、ctypes.cdll.LoadLibrary
で直接読み込んでしまおう!ということで。
Github: aws-lambda-ja-tokenizer マージ済みです。